/*
 * Copyright (c) 2008-2009, Computational Crawling LP
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 *    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *    * Neither the name of Computational Crawling LP, 80legs, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.eightylegs.customer.default80legs;

/**
 * Class ClientUtility.
 * 
 * These are utilities we are providing to 80legs customers to help with WebAnalysis development - most of these functions are related to serialization to and from byte[] 
 */
public class ClientUtility 
{
	private static final int MAX_MESSAGE_COUNT = 1000;
	
	private static int messageCount = 0;
	/**
	 * This is just a simple message output class to limit the messages that come out of your processDocument() function.
	 * Use this instead of System.out directly
	 * 
	 * @param message   The raw byte content of your data.
	 * 
	 */
	public static void outMessage ( String message ) {
		if ( messageCount < MAX_MESSAGE_COUNT )
			System.out.println ( message );
		messageCount++;
	}
	
	/**
	 * Convert a 64-bit long into the 8 bytes in b starting at byteStart
	 * 
	 * @param l   The long to be converted
	 * @param b   The destination byte[]
	 * @param byteStart   The index into b where l should be written
	 * 
	 * @throws Exception if it tries to write past the end of b
	 */
	public static final void longToByteArray(long l, byte[] b, int byteStart) throws Exception {
		if ( byteStart+7 >= b.length )
			throw ( new Exception("Exception in longToByteArray() - byteStart=" + byteStart + " and b.length=" + b.length) );
		b[byteStart+7] = (byte)((l>>56) & 0xFF);
		b[byteStart+6] = (byte)((l>>48) & 0xFF);
		b[byteStart+5] = (byte)((l>>40) & 0xFF);
		b[byteStart+4] = (byte)((l>>32) & 0xFF);
		b[byteStart+3] = (byte)((l>>24) & 0xFF);
		b[byteStart+2] = (byte)((l>>16) & 0xFF);
		b[byteStart+1] = (byte)((l>>8) & 0xFF);
		b[byteStart+0] = (byte)(l & 0xFF);
	}
	
	/**
	 * Convert a 32-bit int into the 4 bytes in b starting at byteStart
	 * 
	 * @param i   The int to be converted
	 * @param b   The destination byte[]
	 * @param byteStart   The index into b where i should be written
	 * 
	 * @throws Exception if it tries to write past the end of b
	 */
	public static final void intToByteArray(int i, byte[] b, int byteStart) throws Exception {
		if ( byteStart+3 >= b.length )
			throw ( new Exception("Exception in intToByteArray() - byteStart=" + byteStart + " and b.length=" + b.length) );
		b[byteStart+3] = (byte)((i>>24) & 0xFF);
		b[byteStart+2] = (byte)((i>>16) & 0xFF);
		b[byteStart+1] = (byte)((i>>8) & 0xFF);
		b[byteStart+0] = (byte)(i & 0xFF);
	}
	
	/**
	 * Convert a 16-bit short into the 2 bytes in b starting at byteStart
	 * 
	 * @param s   The short to be converted
	 * @param b   The destination byte[]
	 * @param byteStart   The index into b where s should be written
	 * 
	 * @throws Exception if it tries to write past the end of b
	 */
	public static final void shortToByteArray(short s, byte[] b, int byteStart) throws Exception {
		if ( byteStart+1 >= b.length )
			throw ( new Exception("Exception in shortToByteArray() - byteStart=" + byteStart + " and b.length=" + b.length) );
		b[byteStart+1] = (byte)((s>>8) & 0xFF);
		b[byteStart+0] = (byte)(s & 0xFF);
	}
	
	/**
	 * Convert the 8 bytes in b starting at byteStart into a 64-bit long
	 * 
	 * @param b   The source byte[]
	 * @param byteStart   The index into b where l should be read
	 * 
	 * @return the long created from the 8 bytes
	 * @throws Exception 
	 * @throws Exception if it tries to read past the end of b
	 */
	public static final long byteArrayToLong(byte[] b, int byteStart) throws Exception {
		if ( byteStart+7 >= b.length )
			throw ( new Exception("Exception in byteArrayToLong() - byteStart=" + byteStart + " and b.length=" + b.length) );
        long val = ((long)b[byteStart+7] << 56)
                + (((long)b[byteStart+6] & 0xFF) << 48)
                + (((long)b[byteStart+5] & 0xFF) << 40)
                + (((long)b[byteStart+4] & 0xFF) << 32)
                + (((long)b[byteStart+3] & 0xFF) << 24)
                + (((long)b[byteStart+2] & 0xFF) << 16)
                + (((long)b[byteStart+1] & 0xFF) << 8)
                + ((long)b[byteStart+0] & 0xFF);
        return val;
	}

	/**
	 * Convert the 4 bytes in b starting at byteStart into a 32-bit int
	 * 
	 * @param b   The source byte[]
	 * @param byteStart   The index into b where i should be read
	 * 
	 * @return the int created from the 4 bytes
	 * @throws Exception if it tries to read past the end of b
	 */
	public static final int byteArrayToInt(byte[] b, int byteStart) throws Exception {
		if ( byteStart+3 >= b.length )
			throw ( new Exception("Exception in byteArrayToInt() - byteStart=" + byteStart + " and b.length=" + b.length) );
        return ((int)b[byteStart+3] << 24)
                + (((int)b[byteStart+2] & 0xFF) << 16)
                + (((int)b[byteStart+1] & 0xFF) << 8)
                + ((int)b[byteStart+0] & 0xFF);
	}

	/**
	 * Convert numBytes bytes in b starting at byteStart into a String using UTF-8 encoding
	 * 
	 * @param b   The source byte[]
	 * @param byteStart   The index into b where i should be read
	 * @param numBytes    The total number of bytes to read
	 * 
	 * @return the String created from the bytes with UTF-8 encoding
	 * @throws Exception if it tries to read past the end of b
	 */
	public static final String byteArrayToString(byte[] b, int byteStart, int numBytes) throws Exception
	{
		return new String ( b, byteStart, numBytes, "UTF-8" );
	}
	
	/**
	 * Convert inString into a byte[] using UTF-8 encoding
	 * 
	 * @param inString  The source String to be converted
	 * 
	 * @return the byte[] created from the string using UTF-8 encoding
	 * @throws Exception if it tries to read past the end of b
	 */
	public static final byte[] stringToByteArray( String inString ) throws Exception
	{
		return inString.getBytes("UTF-8");
	}
	
	/**
	 * Add the bytes from the UTF-8 representation of inString into outputarray at the location byteStart.  If writeSize is true, it writes the 4-byte size before the bytes for the string.
	 * 
	 * @param inString  The source String to be converted
	 * @param outputArray The destination byte[]
	 * @param byteStart The start location to write the bytes
	 * @param writeSize A flag that, if true, causes the size of the string to be written before the string bytes in the output as a 4-byte integer
	 * 
	 * @return the size of the data written into outputArray (either the size of the string's bytes or the size of the string's bytes plus 4 if writeSize is true)
	 * @throws Exception if it tries to read past the end of b
	 */
	public static final int stringToByteArray( String inString, byte[] outputArray, int byteStart, boolean writeSize ) throws Exception
	{
		int retSize = 0;
		byte[] tmpArray = inString.getBytes("UTF-8");
		if ( writeSize == true )
		{
			intToByteArray(tmpArray.length, outputArray, byteStart);
			byteStart += 4;
			retSize += 4;
		}
		System.arraycopy(tmpArray, 0, outputArray, byteStart, tmpArray.length);
		retSize += tmpArray.length;
		
		return retSize;
	}
}
